﻿// Copyright 2010 Maxime Serrano <lastchronicler@gmail.com>
using System;
using System.IO;

namespace MCSharp.Network.Handlers
{
    public class HandshakeHandler : PacketHandler
    {
        #region PacketHandler Members

        public void Handle(BinaryReader reader, Session session, Player player)
        {
            // Get name from handshake
            string handshakeName = PacketReader.ReadString(reader);
            Logger.Log("Session.LoginSequence: Handshake received. Name: {0}", LogType.Debug, handshakeName);

            // Reply to handshake with Server ID or password request
            if (Config.GetBool(ConfigKey.EnablePassword))
                session.Write(PacketWriter.MakeHandshake("+"));
            else if (Config.GetBool(ConfigKey.VerifyNames))
                session.Write(PacketWriter.MakeHandshake(Server.ServerID));
            else
                session.Write(PacketWriter.MakeHandshake("-"));
        }

        #endregion
    }

    public class LoginHandler : PacketHandler
    {
        #region PacketHandler Members

        public void Handle(BinaryReader reader, Session session, Player player)
        {
            // check protocol version
            int clientProtocolVersion = PacketWriter.SwapEndian((uint)reader.ReadInt32());
            if (clientProtocolVersion != Config.ProtocolVersion)
            {
                Logger.Log("Session.LoginSequence: Wrong protocol version: {0}.", LogType.SuspiciousActivity, clientProtocolVersion);
                session.KickNow("Incompatible protocol version!");
                return;
            }

            // Get login username
            string loginUsername = PacketReader.ReadString(reader);
            string loginPassword = PacketReader.ReadString(reader);
            Logger.Log("Session.LoginSequence: Login received. Username: {0}", LogType.Debug, loginUsername);
            Logger.Log("Session.LoginSequence: Login received. Password: {0}", LogType.Debug, loginPassword);

            // Check if Whitelist mode is enabled and if player is on whitelist
            if (Config.GetBool(ConfigKey.Whitelist)) {
                if (!Server.whitelist.Contains(loginUsername.ToLower())) {
                    if (loginUsername != "Player") {
                        KickNow("Not on whitelist!");
                        return;
                    }
                }
            }

            // Check for offline mode
            if (loginUsername == "Player") {
                KickNow("Your client is in offline mode. Please restart it to join.");
                return;
            }

            // Verify password (if set)
            string serverPassword = Config.GetString(ConfigKey.Password);
            if (serverPassword.Length > 0 && loginPassword != serverPassword)
            {
                Logger.Log("Session.LoginSequence: Incorrect password: {0}", LogType.SuspiciousActivity, loginPassword);
                session.KickNow("Incorrect password!");
                return;
            }

            // check name for nonstandard characters
            if (!Player.IsValidName(loginUsername))
            {
                Logger.Log("Session.LoginSequence: Unacceptible player name: {0} ({1})", LogType.SuspiciousActivity, loginUsername, session.GetIP().ToString());
                session.KickNow("Invalid characters in player name!");
                return;
            }

            player = new Player(loginUsername, session, Server.world.spawn);    // TEMPORARY UNTIL WORLD SUPPORT
            player.homePos = Server.world.spawn;

            // check if another player with the same name is on
            Player potentialClone = Server.FindPlayer(player.name);
            if (potentialClone != null)
            {
                player.info.ProcessFailedLogin(player);
                Logger.Log("Session.LoginSequence: Player {0} tried to log in from two computers at once.", LogType.SuspiciousActivity, player.name);
                potentialClone.Message("Warning: someone just attempted to log in using your name.");
                session.KickNow("Already connected from elsewhere!");
                return;
            }

            // check if another player with the same IP is on
            if (Config.GetBool(ConfigKey.LimitOneConnectionPerIP))
            {
                potentialClone = Server.FindPlayer(session.GetIP());
                if (potentialClone != null)
                {
                    player.info.ProcessFailedLogin(player);
                    Logger.Log("Session.LoginSequence: Player {0} tried to log in from same IP ({1}) as {2}.", LogType.SuspiciousActivity,
                        player.name, session.GetIP().ToString(), potentialClone.name);
                    potentialClone.Message("Warning: someone just attempted to log in using your IP.");
                    session.KickNow("Only one connection per IP allowed!");
                    return;
                }
            }

            // Register player for future block updates
            if (!Server.RegisterPlayer(player))
            {
                session.KickNow("Sorry, server is full (" + Server.playerList.Length + "/" + Config.GetInt(ConfigKey.MaxPlayers) + ")");
                return;
            }
            session.hasRegistered = true;

            player.info.ProcessLogin(player);

            // Player is now authenticated. Send server info.
            session.Write(PacketWriter.MakeLogin(player, Config.GetString(ConfigKey.ServerName), Config.GetString(ConfigKey.MOTD)));

            Server.ShowPlayerConnectedMessage(player);

            // Send initial world chunks
            player.UpdateChunks();

            // Set world time
            player.Send(PacketWriter.MakeTimeUpdate(Server.world.time));

            // Spawn to other players, and make them spawn to him
            Server.SendToAll(PacketWriter.MakeEntitySpawn(player), player);
            foreach (Player ply in Server.playerList)
            {
                if (ply != player)
                    session.Send(PacketWriter.MakeEntitySpawn(ply));
            }

            // Welcome message
            if (player.info.timesVisited > 1)
                player.Message("Welcome back to " + Config.GetString(ConfigKey.ServerName));
            else
                player.Message("Welcome to " + Config.GetString(ConfigKey.ServerName));

            player.Message(String.Format("Your player class is {0}{1}{2}. Type {3}/help{2} for details.",
                                           player.info.playerClass.color,
                                           player.info.playerClass.name,
                                           Color.Sys,
                                           Color.Help));

            if (!Config.GetBool(ConfigKey.AllowTime))
                player.Message(Color.Sys + "Time is currently disabled.");

            player.Message(Color.Sys + "There are " + (Server.GetPlayerCount() - 1) + " other players online.");
            player.Message(Color.Sys + "To view who's online, type " + Color.Help + "/players");
        }

        #endregion
    }
}
